<?php

namespace plugin\admin\app\traits;

use app\service\excel\EasyExcelService;
use app\service\jt808\JT808Service;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Exception\UnsupportedTypeException;
use Exception;
use ModbusTcpClient\Network\BinaryStreamConnection;
use plugin\admin\app\model\IotDeviceType;
use plugin\admin\app\model\IotSignal;
use support\exception\BusinessException;
use support\Request;
use support\Response;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Mqtt\Client;
use Workerman\Worker;

trait IotDevice
{

    /**
     * Excel导入数据
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function import_excel(Request $request): Response
    {
        if ($request->method() === 'POST') {
            $file = current($request->file());
            if (!$file || !$file->isValid()) {
                return $this->json(1, '未找到文件');
            }
            $getUploadExtension = $file->getUploadExtension();
            if (!in_array($getUploadExtension, ["xls", "xlsx"])) {
                return errorMessage("只允许上传Excel文件");
            }
            $file_name = $file->getUploadName();
            $file_path = public_path() . '/excel/' . $file_name;
            $file->move($file_path);
            $EasyExcelService = new EasyExcelService();
            try {
                $getCertainRow = $EasyExcelService->getCertainRow($file_path);
                return successMessage("操作成功", $getCertainRow);
            } catch (IOException|UnsupportedTypeException $e) {
                return errorMessage($e->getMessage());
            }
        }
        return view('iot-device/import_excel');
    }

    /**
     * 连接
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function connect(Request $request)
    {
        $id = $request->post('id', '');
        if (empty($id)) {
            return errorMessage("参数有误");
        }
        $find = $this->model->select(['id', 'ip', 'port', 'device_type_id', 'model', 'is_lecp_server', 'lecp_server_name'])->find($id);
        if (empty($find)) {
            return errorMessage("数据不存在");
        }
        $ip = $find->ip;
        $is_lecp_server = $find->is_lecp_server;
        $lecp_server_name = $find->lecp_server_name;
        $port = $find->port;
        $device_type_id = $find->device_type_id;
        $first = IotDeviceType::where('id', $device_type_id)->where('pid', '<>', 0)->select(['id', 'name', 'alias'])->first();
        if (empty($first)) {
            return errorMessage("协议类型不存在,请重新绑定");
        }
        $name = $first->name;
        $alias = $first->alias;
        $error = $this->setProtocolProcessing($id, $name, $alias, $ip, $port, $is_lecp_server, $lecp_server_name);
        if (is_string($error)) {
            return errorMessage($error);
        }
        return successMessage();
    }

    /**
     * 操作合集
     * @param int $id
     * @param string $name
     * @param string $alias
     * @param string $ip
     * @param int $port
     * @param int $is_lecp_server
     * @param string $lecp_server_name
     * @return string|true
     * @throws Exception
     */
    private function setProtocolProcessing(int $id, string $name, string $alias, string $ip, int $port, int $is_lecp_server, string $lecp_server_name)
    {
        $date = date("Y-m-d H:i:s");
        switch ($alias) {
            case "modbus":
                //作为客户端去链接
                $connection = BinaryStreamConnection::getBuilder()
                    ->setPort($port)
                    ->setHost($ip)
                    ->build();
                if ($name == "ModbusTcp") {
                    try {
                        $connection->connect();
                        $this->model->where('id', $id)->update(['status' => "online", 'updated_time' => $date]);
                    } catch (\Exception $exception) {
                        //连接不上表示已经离线
                        $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                        return "连接失败";
                    } finally {
                        $connection->close();
                    }
                } elseif ($name == "ModbusRtu") {
                    $device_sn = $this->model->where('id', $id)->value('device_sn');
                    try {
                        $device = $device_sn;
                        $fd = fopen($device, 'w+b');
                        $sttyModes = implode(' ', [
                            'cs8', // enable character size 8 bits
                            '9600', // enable baud rate 9600
                            '-cstopb', // 1 stop bit
                            '-parenb', // parity none

                            '-icanon', // disable enable special characters: erase, kill, werase, rprnt
                            'min 0', // with -icanon, set N characters minimum for a completed read
                            'ignbrk', // enable ignore break characters
                            '-brkint', // disable breaks cause an interrupt signal
                            '-icrnl', // disable translate carriage return to newline
                            '-imaxbel', // disable beep and do not flush a full input buffer on a character
                            '-opost', // disable postprocess output
                            '-onlcr', // disable translate newline to carriage return-newline
                            '-isig', // disable interrupt, quit, and suspend special characters
                            '-iexten', // disable non-POSIX special characters
                            '-echo', // disable echo input characters
                            '-echoe', // disable echo erase characters as backspace-space-backspace
                            '-echok', // disable echo a newline after a kill character
                            '-echoctl', // disable same as [-]ctlecho
                            '-echoke', // disable kill all line by obeying the echoprt and echoe settings
                            '-noflsh', // disable flushing after interrupt and quit special characters
                            '-ixon', // disable XON/XOFF flow control
                            '-crtscts', // disable RTS/CTS handshaking
                        ]);
                        $sttyResult = exec("stty -F {$device} {$sttyModes}");
                        if ($sttyResult === false) {
                            $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                        } else {
                            $this->model->where('id', $id)->update(['status' => "online", 'updated_time' => $date]);
                        }
                    } catch (Exception $exception) {
                        $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                        return $exception->getMessage();
                    } finally {
                        fclose($fd);
                    }
                }
                break;
            case "plc":
                $action = "plc_read_node";
                if ($lecp_server_name && $is_lecp_server == 1) {
                    //LECPServer 服务插件,如果配置地址表示不在线
                    $address = IotSignal::where('model_id', $id)->value('address');
                    if ($address) {
                        $uri = "http://{$ip}:{$port}";
                        $node = "NODES.{$lecp_server_name}.{$address}";
                        $data = json_encode(['action' => $action, 'node' => $node]);
                        $rtval = http_post($uri, $data);
                        if (empty($rtval)) {
                            $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                        } else {
                            $this->model->where('id', $id)->update(['status' => "online", 'updated_time' => $date]);
                        }
                    } else {
                        $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                        return "未配置PLC对应的地址";
                    }
                } else {
                    return "未定义各自设备的协议实现";
                }
                break;
            case "tcp":
                try {
                    // 异步建立一个到实际tcp服务器的连接
                    $connection_tcp = new AsyncTcpConnection("tcp://" . $ip . ":" . $port);
                    $connection_tcp->onConnect = function (AsyncTcpConnection $connection_tcp) use ($id, $date) {
                        $this->model->where('id', $id)->update(['status' => "online", 'updated_time' => $date]);
                    };
                    $connection_tcp->onMessage = function (AsyncTcpConnection $connection_tcp, $buffer) use ($id) {
                        //todo 具体的根据业务进行操作
                        $model_id = $this->model->where('id', $id)->value('model');
                        $data_model = IotSignal::where('model_id', $model_id)->select(['id', 'message', 'field_type', 'field_name', 'encode'])->get();
                        //对16进制数据进行处理
                        $data = hex2bin($buffer);
                        var_dump(json_decode($data, true));
                        $send_data = json_encode(['type' => 'huida']);
                        $connection_tcp->send(bin2hex($send_data));
                    };
                    $connection_tcp->onClose = function (AsyncTcpConnection $connection_tcp) use ($id, $date) {
                        $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                    };
                    $connection_tcp->onError = function (AsyncTcpConnection $connection_tcp) use ($id, $date) {
                        $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                    };
                    // 执行异步连接
                    $connection_tcp->connect();

                } catch (Exception $exception) {
                    $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                    return $exception->getMessage();
                }
                break;
            case "mqtt":
                $options = [];
                $mqtt = new Client("mqtt://{$ip}:{$port}", $options);
                $mqtt->onConnect = function ($mqtt) use ($id, $date) {
                    $this->model->where('id', $id)->update(['status' => "online", 'updated_time' => $date]);
                };
                $mqtt->onClose = function ($mqtt) use ($id, $date) {
                    $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                    $mqtt->close();
                };
                $mqtt->connect();
                break;
            case "jt808":
                $connection = new Worker("tcp://{$ip}:{$port}");
                // 连接成功
                $connection->onConnect = function ($connection) use ($id, $date) {
                    $this->model->where('id', $id)->update(['status' => "online", 'updated_time' => $date]);
                    //心跳
                    $msg = '7e0002000001453409748303c8477e7e0200003a0145340972520bdb00000000000000030205d4b406b9efe800ed00000120200430194209010400000716eb16000c00b28986043103188076978800060089fffffffff77e';
                    $data = hex2bin($msg);
                    $connection->send($data);
                };
                $connection->onMessage = function ($connection, $data) {
                    $data1 = bin2hex($data);
                    $JT808Service = new JT808Service();
                    $data16Arrays = $JT808Service->getTo16Bytes($data);
                    foreach ($data16Arrays as $key => $data16Array) {
                        //获取消息id
                        $MessageId = $JT808Service->getMessageIdNumber($data16Array);
                        var_dump($MessageId);
                        //设备号
                        $equipmentNumber = $JT808Service->getEquipmentNumber($data16Array);
                        //位置信息上报获取
                        if ($MessageId == '0200' && $equipmentNumber) {
                            //报警信息
                            $AlarmMessage = $JT808Service->getAlarmMessage($data16Array, 13, true);
                            //状态
                            $status = $JT808Service->getPositionStatus($data16Array, 17, true);
                            //经度
                            $Latitude = $JT808Service->getLatitude($data16Array, 21, 'i');
                            //纬度
                            $Longitude = $JT808Service->getLongitude($data16Array, 25, 'i');
                            //高度
                            $Height = $JT808Service->getHeight($data16Array, 29);
                            //速度
                            $Speed = $JT808Service->getSpeed($data16Array, 31);
                            //方向
                            $Direction = $JT808Service->getDirection($data16Array, 33);
                            //时间
                            $Datetime = $JT808Service->getDatetime($data16Array, 35);
                            if ($Latitude && $Longitude) {
                                //执行你的逻辑
                            }
                            var_dump($Latitude, $Longitude);
                        } elseif ($MessageId == '0102') {
                            //终端鉴权
                            $s = $JT808Service->getAuthenticationCode($data16Array);
                            var_dump($s);
                        } elseif ($MessageId == '0104') {
                            //查询终端参数应答

                        } elseif ($MessageId == '0100') {
                            // 终端注册
                            $s = $JT808Service->getTerminalRegistration($data16Array);
                            var_dump($s);
                        } elseif ($MessageId == '0002') {
                            // 终端心跳--TCP消息应答超时时间,单位为秒(s)

                        } elseif ($MessageId == '0001') {
                            // 终端通用应答 终端心跳发送间隔,单位为秒(s)
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0003') {
                            // 终端注销
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8001') {
                            // 平台通用应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8100') {
                            // 终端注册应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8103') {
                            // 设置终端参数
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8104') {
                            // 查询终端参数
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8105') {
                            // 终端控制
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8201') {
                            // 位置信息查询
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0201') {
                            // 位置信息查询应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8202') {
                            // 临时位置跟踪控制
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8300') {
                            // 文本信息下发
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8301') {
                            //  事件设置
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0301') {
                            //   事件报告
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8302') {
                            //   提问下发
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0302') {
                            //提问应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8303') {
                            //信息点播菜单设置
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0303') {
                            //信息点播/取消
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8304') {
                            //信息服务
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8400') {
                            // 电话回拨
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8401') {
                            // 设置电话本
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8500') {
                            // 车辆控制
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0500') {
                            // 车辆控制应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8600') {
                            // 设置圆形区域
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8601') {
                            // 删除圆形区域
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8602') {
                            // 设置矩形区域
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8603') {
                            // 删除矩形区域
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8604') {
                            // 设置多边形区域
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8605') {
                            // 删除多边形区域
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8606') {
                            // 设置路线
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8607') {
                            // 删除路线
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8700') {
                            // 行驶记录数据采集命令
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0700') {
                            // 行驶记录数据上传
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8101') {
                            // 行驶记录参数下传命令
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0701') {
                            // 电子运单上报
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0702') {
                            // 驾驶员身份信息采集上报
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0800') {
                            // 多媒体事件信息上传
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0801') {
                            // 多媒体数据上传
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8800') {
                            // 多媒体数据上传应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8801') {
                            // 摄像头立即拍摄命令
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8802') {
                            // 存储多媒体数据检索
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0802') {
                            // 存储多媒体数据检索应答
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8803') {
                            //  存储多媒体数据上传命令
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8804') {
                            //  录音开始命令
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8900') {
                            //  数据下行透传
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0900') {
                            //  数据上行透传
                            //var_dump($data16Array);
                        } elseif ($MessageId == '0901') {
                            // 数据压缩上报
                            //var_dump($data16Array);
                        } elseif ($MessageId == '8A00') {
                            // 平台 RSA 公钥
                            //var_dump($data16Array);
                        } elseif ($MessageId == 'x0A00') {
                            // 终端 RSA 公钥
                            //var_dump($data16Array);
                        }
                        //发送给客户端
                        $sendClientData = $JT808Service->getVerifyNumberArray($data16Array);
                        $connection->send($sendClientData);
                    }
                };
                $connection->onClose = function ($connection) use ($id, $date) {
                    $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                };
                $connection->onError = function ($connection) use ($id, $date) {
                    $this->model->where('id', $id)->update(['status' => "offline", 'updated_time' => $date]);
                };
                $connection->listen();
                break;
            default:
                break;
        }
        return true;
    }

    public function debugging(Request $request): Response
    {
        $device_id = $request->get('id', '');
        if (empty($device_id)) {
            return errorMessage("参数有误");
        }
        $data_device = $this->model->where(['id' => $device_id])->first();
        if (empty($data_device)) {
            return errorMessage("设备不存在");
        }
        if ($request->method() == "POST") {
            $post = $request->post();
            if (empty($post)) {
                return errorMessage("参数有误");
            }
            $url = $post['url'];
            $zhuti = $post['zhuti'];
            $address = $post['address'];
            $address_value = $post['address_value'] ?? "";
            $read = $post['read'];
            $node = "NODES.{$zhuti}.{$address}";
            if ($read == "plc_read_node") {
                $data = json_encode(['action' => $read, 'node' => $node]);
            } else {
                if (empty($address_value)) {
                    return errorMessage("值不能为空");
                }
                $data = json_encode(['action' => $read, 'node' => $node, "value" => [$address_value]]);
            }
            $get_data = http_post($url, $data);
            if (empty($get_data)) {
                return errorMessage("获取失败");
            }
            if ($get_data['errcode'] != 0) {
                return errorMessage("获取失败");
            }
            return successMessage("获取成功", [$get_data]);
        }

        return view('iot-device/debugging', ['device_id' => $device_id]);
    }
}
